Intro

우리가 시스템을 개발할 때 모두 직접 개발하지 않고, 패키지를 사던지 오픈소스를 이용한다.
또는 사내 다른 팀이 제공하는 컴포넌트를 사용한다.
이렇게 외부 환경과의 상호작용 방식을 정의하는 것이 시스템 경계(System Boundaries)라고 생각한다.

외부 코드 사용하기

인터페이스 제공자는 적용성을 최대한 넓히려 애쓴다.
인터페이스 사용자는 자신의 요구에 집중하는 인터페이스를 바란다.
여기서는 예시로 java.util.Map을 들고 있다.

Map sensors = new HashMap();

Sensor s = (Sensor)sensors.get(sensorId);

Sensor라는 객체를 담는 Map을 생성하고,
Seonsor 객체가 필요한 코드는 Sensor 객체를 가져온다.
이 코드는 깨끗한 코드가 아니고, 의도도 분명히 드러나지 않는다. 제네릭스(Generics)를 사용하면 코드 가독성이 크게 높아진다.

Map<String, Sensor> sensors = new HashMap<String, Sensor>();
...
Sensor s = sensors.get(sensorId);

하지만 이 방법도 ”Map<String, Sensor>가 사용자에게 필요하지 않은 기능까지 제공한다.“는
문제를 해결하지 못한다.

public class Sensors {
    private Map sensors = new HashMap();

    public Sensor getById(String id) {
        return (Sensor) sensors.get(id);
    }
    ...
}

이렇게 하면 사용자는 제네릭스가 사용되었는지 여부를 신경 쓸 필요가 없다.
Sensor 클래스 안에서 객체 유형을 관리하고 변환하기 때문이다.
이렇게 캡슐화를 통해 사용하는 것도 좋다.

  • 주의할 점
    • 여기저기 넘기지 말자.
    • 클래스나 클래스 계열 밖으로 노출되지 않도록 주의하자.
    • Map 인스턴스를 공개 API의 인수로 넘기거나 반환값으로 사용하지 말자.

경계 살피고 익히기

외부 코드를 익히기는 어렵다.
외부 코드를 통합하기도 어렵다.
두 가지를 동시에 하기는 두 배나 어렵다.
먼저 간단한 테스트 케이스를 작성해 외부 코드를 익한다면?
짐 뉴커크는 이를 학습 테스트라 부른다.

학습 테스트는 프로그램에서 사용하려는 방식대로 외부 API를 호출한다.
학습 테스트는 API를 사용하려는 목적에 초점을 둔다.

log4j 익히기

위 내용을 토대로 log4j를 익혀보자.

학습 테스트는 공짜 이상이다.

학습 테스트는 패키지가 예상대로 도는지 검증한다.
패키지 새 버전이 나올 때마다 새로운 위험이 생긴다.
새 버전이 우리 코드와 호환되지 않다면, 학습 테스트가 바로 알아낸다.
이런 경계 테스트가 있다면 패키지의 새 버전으로 이전하기 쉬워진다.
그렇지 않다면 낡은 버전을 필요 이상으로 오랫동안 사용하려는 유혹에 빠지기 쉽다.

패키지가 한 두개가 아니고 우리 코드가 다양한데 테스트 케이스를 다 만들 수 있을까…?

아직 존재하지 않는 코드를 사용하기

때로는 우리 지식이 경계를 너머 미치지 못하는 코드 영역도 있다.
때로는 더 이상 내다보지 않기로 결정한다.
즉, 모르는 분야를 나중에 부딪히는 것이다.

인터페이스를 정의하고, 클래스와 매서드를 추가한다.
Controller를 분리하여 코드를 깔끔하게 한다.
다른 팀이 구현을 하면 간극을 매우게 된다.
ADAPTER 패턴으로 API 사용을 캡슐화해 API가 바뀔 때 수정할 코드를 한 곳으로 모은다.

이와 같은 설계는 테스트도 편하다.

저자의 자기 자랑 같이 들린 파트…

깨끗한 경계

깨끗한 경계를 위하여!

  1. 외부 패키지를 호출하는 코드를 가능한 줄여 경계를 관리하자.
  2. 새로운 클래스로 경계를 감싸거나 ADPAPTER 패턴을 사용해

우리가 원하는 인터페이스를 패키지가 제공하는 인터페이스로 변환하자.

이렇게 좋아진다!

  1. 코드 가독성이 높아진다.
  2. 경계 인터페이스를 사용하는 일관성도 높아진다.
  3. 외부 패키지가 변했을 때 변경할 코드도 줄어든다.